package com.fa4j.system.login.controller;


import com.fa4j.common.base.model.CommonState;
import com.fa4j.common.web.annotation.ApiMethod;
import com.fa4j.common.web.model.AppType;
import com.fa4j.common.web.model.ClientInfo;
import com.fa4j.common.web.util.ContextUtil;
import com.fa4j.system.account.model.*;
import com.fa4j.system.account.repository.AccountBindRepository;
import com.fa4j.system.account.service.AccountBindService;
import com.fa4j.system.account.service.AccountPasswordService;
import com.fa4j.system.account.service.AccountService;
import com.fa4j.system.app.model.AppMenu;
import com.fa4j.system.app.repository.AppMenuRepository;
import com.fa4j.system.app.repository.AppRoleRepository;
import com.fa4j.system.login.convert.LoginConverter;
import com.fa4j.system.login.dto.LoginSessionDTO;
import com.fa4j.system.login.model.*;
import com.fa4j.system.login.service.CaptchaService;
import com.fa4j.system.login.service.LoginPropsService;
import com.fa4j.system.login.service.LoginService;
import com.fa4j.system.login.service.LoginSessionService;
import com.fa4j.system.login.util.LoginUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

@Slf4j
@RestController
@RequestMapping("system/login")
@RequiredArgsConstructor
public class LoginController {

    private final LoginService loginService;
    private final LoginSessionService loginSessionService;
    private final CaptchaService captchaService;
    private final AccountService accountService;
    private final AccountPasswordService accountPasswordService;
    private final AccountBindService accountBindService;
    private final LoginPropsService loginPropsService;

    private final LoginConverter loginConverter;

    private final AppMenuRepository appMenuRepository;
    private final AppRoleRepository appRoleRepository;
    private final AccountBindRepository accountBindRepository;


    @ApiMethod(name = "系统登录-登录方式查询", type = {AppType.MANAGE, AppType.CLIENT}, login = ApiMethod.Switch.OFF)
    @GetMapping("settings")
    public LoginSettings queryLoginSettingInfo(@Parameter(hidden = true) ClientInfo clientInfo, String redirectUri) {
        return loginPropsService.queryLoginSettings(clientInfo.getClientAppType(), redirectUri);
    }

    @ApiMethod(name = "系统登录-账号登录", type = {AppType.MANAGE, AppType.CLIENT}, login = ApiMethod.Switch.OFF)
    @PostMapping
    public LoginInfo login(@Parameter(hidden = true) ClientInfo clientInfo,
                           @Valid @RequestBody LoginCmd cmd) {
        LoginSession session = loginService.doLogin(clientInfo.getClientAppType(), cmd, clientInfo);
        ContextUtil.set(loginConverter.toSessionDTO(session));
        ContextUtil.set(LoginConstants.API_METHOD_RECORD, true);
        return loginConverter.toLoginInfo(session);
    }

    @ApiMethod(name = "系统登录-账号退出", type = {AppType.MANAGE, AppType.CLIENT},
            login = ApiMethod.Switch.ON, auth = ApiMethod.Switch.OFF, record = ApiMethod.Switch.ON)
    @PostMapping("/logout")
    public void logout() {
        loginSessionService.remove(LoginUtil.getSession().getAccessToken());
    }

    @ApiMethod(name = "系统登录-登录信息查询", type = {AppType.MANAGE, AppType.CLIENT},
            login = ApiMethod.Switch.ON, auth = ApiMethod.Switch.OFF, record = ApiMethod.Switch.OFF)
    @GetMapping("/info")
    public LoginInfo queryLoginInfo(boolean all) {
        LoginSessionDTO session = LoginUtil.getSession();
        LoginInfo loginInfo = loginSessionService.get(session.getAccessToken())
                .map(loginConverter::toLoginInfo)
                .orElse(null);
        if (loginInfo != null && all) {
            List<LoginRoleInfo> roles = new ArrayList<>();
            if (!ObjectUtils.isEmpty(session.getAccount().getRoleCodes())) {
                if (session.getAccount().getRoleCodes().contains(LoginConstants.ROLE_ADMIN)) {
                    roles.add(new LoginRoleInfo("管理员", LoginConstants.ROLE_ADMIN));
                } else {
                    roles = appRoleRepository
                            .findByAppAndCodes(AppType.valueOf(session.getAppType()), session.getAccount().getRoleCodes())
                            .stream()
                            .filter(r -> r.getState() == CommonState.ENABLE)
                            .map(loginConverter::toRoleInfo)
                            .toList();
                }
            }
            loginInfo.setRoles(roles);
            List<AccountBind> binds = accountBindRepository.queryByAccountIdAndType(LoginUtil.getAccountId(), null);
            loginInfo.setBinds(binds);
        }
        return loginInfo;
    }

    @ApiMethod(name = "系统登录-菜单查询", type = {AppType.MANAGE, AppType.CLIENT},
            login = ApiMethod.Switch.ON, auth = ApiMethod.Switch.OFF, record = ApiMethod.Switch.OFF)
    @GetMapping("/menus")
    public List<AppMenu> queryMenu() {
        LoginSessionDTO session = LoginUtil.getSession();
        List<String> roleCodes = session.getAccount().getRoleCodes();
        if (ObjectUtils.isEmpty(roleCodes)) {
            return List.of();
        }
        List<Integer> menuIds = null;
        if (!roleCodes.contains(LoginConstants.ROLE_ADMIN)) {
            menuIds = appRoleRepository.findByAppAndCodes(AppType.valueOf(session.getAppType()), roleCodes)
                    .stream()
                    .filter(role -> role.getState().equals(CommonState.ENABLE))
                    .flatMap(r -> r.getMenuIds().stream()).toList();
        }
        return appMenuRepository.queryTree(AppType.valueOf(session.getAppType()), CommonState.ENABLE, menuIds);
    }


    @ApiMethod(name = "系统登录-发送验证码", type = {AppType.MANAGE, AppType.CLIENT}, login = ApiMethod.Switch.OFF)
    @PostMapping("sendCaptcha")
    @Operation(summary = "发送验证码", description = "发送验证码")
    public CaptchaSendResult sendCaptcha(@RequestBody CaptchaSendCmd cmd) {
        return captchaService.sendCaptcha(cmd);
    }

    @ApiMethod(name = "系统登录-修改登录人信息", type = {AppType.MANAGE, AppType.CLIENT},
            login = ApiMethod.Switch.ON, auth = ApiMethod.Switch.OFF, record = ApiMethod.Switch.ON)
    @PatchMapping("modify")
    public void modify(@RequestBody LoginModifyCmd cmd) {
        if (!ObjectUtils.isEmpty(cmd.getPhone())) {
            captchaService.validCaptcha(CaptchaType.MODIFY_PHONE, cmd.getPhone(), cmd.getCaptchaCode());
        }
        if (!ObjectUtils.isEmpty(cmd.getEmail())) {
            captchaService.validCaptcha(CaptchaType.MODIFY_EMAIL, cmd.getEmail(), cmd.getCaptchaCode());
        }
        AccountModifyCmd accountModifyCmd = loginConverter.toAccountModifyCmd(cmd);
        accountModifyCmd.setId(LoginUtil.getAccountId());
        accountService.modify(accountModifyCmd);
    }

    @ApiMethod(name = "系统登录-修改登录人密码", type = {AppType.MANAGE, AppType.CLIENT},
            login = ApiMethod.Switch.ON, auth = ApiMethod.Switch.OFF, record = ApiMethod.Switch.ON)
    @PostMapping("modifyPwd")
    public void modifyPwd(@RequestBody AccountPwdModifyCmd cmd) {
        cmd.setAccountId(LoginUtil.getAccountId());
        cmd.setType(AccountPasswordType.LOGIN);
        accountPasswordService.modify(cmd);
    }

    @ApiMethod(name = "系统登录-绑定关系", type = {AppType.MANAGE, AppType.CLIENT},
            login = ApiMethod.Switch.ON, auth = ApiMethod.Switch.OFF, record = ApiMethod.Switch.ON)
    @PostMapping("bind")
    public void bind(@Parameter(hidden = true) ClientInfo clientInfo, @RequestBody AccountBindCmd cmd) {
        cmd.setAppType(clientInfo.getClientAppType());
        cmd.setAccountId(LoginUtil.getAccountId());
        accountBindService.bind(cmd);
    }

    @ApiMethod(name = "系统登录-解绑关系", type = {AppType.MANAGE, AppType.CLIENT},
            login = ApiMethod.Switch.ON, auth = ApiMethod.Switch.OFF, record = ApiMethod.Switch.ON)
    @PostMapping("unBind")
    public void unBind(@RequestBody AccountUnBindCmd cmd) {
        cmd.setAccountId(LoginUtil.getAccountId());
        accountBindService.unbind(cmd);
    }

}
